package com.jnc.pay.core.vertx.filter;

import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.crypto.asymmetric.RSA;
import cn.hutool.crypto.asymmetric.Sign;
import cn.hutool.crypto.asymmetric.SignAlgorithm;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.jnc.pay.biz.common.model.Mch;
import com.jnc.pay.biz.common.service.MchService;
import com.jnc.pay.constant.BizConstant;
import com.jnc.pay.constant.RedisConstant;
import com.jnc.pay.constant.SysConstant;
import com.jnc.pay.core.config.redis.RedisStore;
import com.jnc.pay.core.model.BaseResp;
import com.jnc.pay.core.model.RespCode;
import com.jnc.pay.util.IpUtil;
import com.jnc.pay.util.convert.JsonUtil;
import io.vertx.core.Handler;
import io.vertx.core.buffer.Buffer;
import io.vertx.ext.web.RoutingContext;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

/**
 * @Auther: jjn
 * @Date: 2020/7/2 10:49
 * @Desc: 前置处理器
 */
@Slf4j
@Component
public class PreHandler implements Handler<RoutingContext> {

    @Autowired
    private RedisStore redisStore;
    @Autowired
    private MchService mchService;

    /**
     * 前置处理器处理业务：
     * 1、判断客户端ip是否是白名单；
     * 2、进行参数解密及sign验签，同时判断timestamp是否已超时
     *
     * @param context
     */
    @Override
    public void handle(RoutingContext context) {
        String str = context.getBodyAsString(SysConstant.CHARSET_NAME);

        //获取请求path
        String path = context.request().path();

        //获取客户端Ip
        String realIP = IpUtil.getRealIP(context.request());
        log.debug("preHandler request, path: {}, ip: {}, req: {}", path, realIP, str);
        if(StrUtil.isBlank(realIP)){
            context.request().response().end(
                    JsonUtil.bean2Json(BaseResp.resp(RespCode.NOT_WHITE_IP.getCode(), RespCode.NOT_WHITE_IP.getDesc(), null)));
            return;
        }
        //判断客户端IP，是否是白名单
        boolean flag = redisStore.hasHashKey(RedisConstant.DICT_DATA + RedisConstant.WHITE_LIST_IP, realIP);
        if(!flag){
            context.request().response().end(
                    JsonUtil.bean2Json(BaseResp.resp(RespCode.NOT_WHITE_IP.getCode(), RespCode.NOT_WHITE_IP.getDesc(), null)));
            return;
        }
        //解密参数和验签，timestamp是否延时
        JSONObject json = JSONObject.parseObject(str);
        Long mchId = json.getLong("mchId");
        String sign = json.getString("sign");
        String encrypt = json.getString("encrypt");

        if(mchId == null || StrUtil.isBlank(sign) || StrUtil.isBlank(encrypt)){
            context.request().response().end(
                    JsonUtil.bean2Json(BaseResp.resp(RespCode.ILLEGAL_ARGUMENT.getCode(), RespCode.ILLEGAL_ARGUMENT.getDesc(), null)));
            return;
        }

        //校验是否存在有效的商户号
        Mch mchBy = mchService.getMchBy(mchId);
        if(mchBy == null){
            context.request().response().end(
                    JsonUtil.bean2Json(BaseResp.resp(RespCode.INVALID_MCH_ID.getCode(), RespCode.INVALID_MCH_ID.getDesc(), null)));
            return;
        }

        //解密参数并验签
        RSA rsa = new RSA(mchBy.getPriKey(), null);
        byte[] decrypt = rsa.decrypt(Base64.decodeBase64(encrypt), KeyType.PrivateKey);
        String deStr = new String(decrypt);
        log.debug("Params after decryption, path: {}, decrypt req: {}", path, deStr);

        //判断是否过期
        JSONObject reqJson = JSON.parseObject(deStr);
        long timestamp = reqJson.getLong("timestamp");
        long curTime = System.currentTimeMillis();
        if(timestamp < (curTime - BizConstant.TIME_STAMP_OUT))
        if((timestamp + BizConstant.TIME_STAMP_OUT) > curTime || (curTime - BizConstant.TIME_STAMP_OUT) > timestamp){
            context.request().response().end(
                    JsonUtil.bean2Json(BaseResp.resp(RespCode.TIME_STAMP_EXPIRE.getCode(), RespCode.TIME_STAMP_EXPIRE.getDesc(), null)));
            return;
        }
        reqJson.put("clientId", realIP);

        Sign verifySign = new Sign(SignAlgorithm.SHA1withRSA, null, mchBy.getMchPubKey());
        boolean verify = verifySign.verify(StrUtil.bytes(deStr, CharsetUtil.CHARSET_UTF_8), Base64.decodeBase64(sign));
        if(!verify){
            context.request().response().end(
                    JsonUtil.bean2Json(BaseResp.resp(RespCode.SIGN_VERIFY_FAIL.getCode(), RespCode.SIGN_VERIFY_FAIL.getDesc(), null)));
            return;
        }
        log.debug("preHandler request send handler, path: {}, req: {}", path, reqJson.toJSONString());
        context.setBody(Buffer.buffer(reqJson.toJSONString()));
        context.next();
    }
}
